package com.rzjm.exercise.common.utils;

import cn.hutool.core.util.StrUtil;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * OKHttp3
 * <p>
 * Description:
 * </p>
 *
 * @author zhouwei
 * @version v1.0.0
 * @date 2021-08-24 14:05:08
 */
public class OkHttpClientUtil {
	private static Logger LOGGER = LoggerFactory.getLogger(OkHttpClientUtil.class);

	private static final int READ_TIMEOUT = 1000;
	private static final int CONNECT_TIMEOUT = 600;
	private static final int WRITE_TIMEOUT = 600;
	private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
	private static final byte[] LOCKER = new byte[0];
	private static OkHttpClientUtil mInstance;
	private OkHttpClient okHttpClient;

	private OkHttpClientUtil() {
		okhttp3.OkHttpClient.Builder clientBuilder = new okhttp3.OkHttpClient.Builder();
		// 读取超时
		clientBuilder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
		// 连接超时
		clientBuilder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
		//写入超时
		clientBuilder.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
		okHttpClient = clientBuilder.build();
	}

	/**
	 * 单例模式获取 NetUtils
	 *
	 * @return {@link OkHttpClientUtil}
	 */
	public static OkHttpClientUtil getInstance() {
		if (mInstance == null) {
			synchronized (LOCKER) {
				if (mInstance == null) {
					mInstance = new OkHttpClientUtil();
				}
			}
		}
		return mInstance;
	}

	/**
	 * GET，同步方式，获取网络数据
	 *
	 * @param url 请求地址
	 * @return {@link Response}
	 */
	public Response getData(String url) {
		// 构造 Request
		Request.Builder builder = new Request.Builder();
		Request request = builder.get().url(url).build();
		// 将 Request 封装为 Call
		Call call = okHttpClient.newCall(request);
		// 执行 Call，得到 Response
		Response response = null;
		try {
			response = call.execute();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return response;
	}

	/**
	 * POST 请求，同步方式，提交数据
	 *
	 * @param url        请求地址
	 * @param bodyParams 请求参数
	 * @return {@link Response}
	 */
	public Response postData(String url, Map<String, Object> bodyParams) {
		// 构造 RequestBody
		RequestBody body = setRequestBody(bodyParams);
		// 构造 Request
		Request.Builder requestBuilder = new Request.Builder();
		Request request = requestBuilder.post(body).url(url).build();
		// 将 Request 封装为 Call
		Call call = okHttpClient.newCall(request);
		// 执行 Call，得到 Response
		Response response = null;
		try {
			response = call.execute();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return response;
	}

	/**
	 * POST 请求，同步方式，提交数据
	 *
	 * @param url        请求地址
	 * @param bodyParams 请求参数
	 * @return {@link Response}
	 */
	public Response postData(String url, Map<String, Object> bodyParams, String authorization) {
		// 构造 RequestBody
		RequestBody body = setRequestBody(bodyParams);
		// 构造 Request
		Request.Builder requestBuilder = new Request.Builder();
		Request request = requestBuilder.post(body).addHeader("Authorization", authorization).url(url).build();
		// 将 Request 封装为 Call
		Call call = okHttpClient.newCall(request);
		// 执行 Call，得到 Response
		Response response = null;
		try {
			response = call.execute();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return response;
	}

	/**
	 * GET 请求，异步方式，获取网络数据
	 *
	 * @param url       请求地址
	 * @param myNetCall 回调函数
	 */
	public void getDataAsync(String url, final MyNetCall myNetCall) {
		// 构造 Request
		Request.Builder builder = new Request.Builder();
		Request request = builder.get().url(url).build();
		// 将 Request 封装为 Call
		Call call = okHttpClient.newCall(request);
		// 执行 Call
		call.enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				myNetCall.failed(call, e);
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				myNetCall.success(call, response);
			}
		});
	}

	/**
	 * POST 请求，异步方式，提交数据
	 *
	 * @param url        请求地址
	 * @param bodyParams 请求参数
	 * @param myNetCall  回调函数
	 */
	public void postDataAsync(String url, Map<String, Object> bodyParams, final MyNetCall myNetCall) {
		// 构造 RequestBody
		RequestBody body = setRequestBody(bodyParams);
		// 构造 Request
		buildRequest(url, myNetCall, body);
	}

	/**
	 * 同步 POST 请求，使用 JSON 格式作为参数
	 *
	 * @param url  请求地址
	 * @param json JSON 格式参数
	 * @return 响应结果
	 * @throws IOException 异常
	 */
	public String postJson(String url, String json) throws IOException {
		RequestBody body = RequestBody.create(JSON, json);
		Request request = new Request.Builder()
				.url(url)
				.post(body)
				.build();
		Response response = okHttpClient.newCall(request).execute();
		if (response.isSuccessful()) {
			return response.body().string();
		} else {
			throw new IOException("Unexpected code " + response);
		}
	}

	/**
	 * 同步 POST 请求，使用 JSON 格式作为参数
	 *
	 * @param url  请求地址
	 * @param jsonBody JSON 格式参数
	 * @return 响应结果
	 * @throws IOException 异常
	 */
	public Response postJson(String url, String jsonBody, Map<String, String> headerMap, Map<String, String> paramsMap) throws IOException {
		Request.Builder requestBuilder = new Request.Builder();
		HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
		//设置header参数
		if (headerMap != null && headerMap.size()>0) {
			for (Map.Entry<String, String> mapEntry : headerMap.entrySet()) {
				requestBuilder.addHeader(mapEntry.getKey(), mapEntry.getValue());
			}
		}
		//设置QueryParameter参数
		if (paramsMap != null && paramsMap.size()>0) {
			for (Map.Entry<String, String> mapEntry : paramsMap.entrySet()) {
				urlBuilder.addEncodedQueryParameter(mapEntry.getKey(), mapEntry.getValue());
			}
		}
		requestBuilder.url(urlBuilder.build());
		//设置body
		if(StrUtil.isNotEmpty(jsonBody)){
			RequestBody body = RequestBody.create(JSON, jsonBody);
			requestBuilder.post(body);
		}
		LOGGER.info("url = {}",urlBuilder.toString());
		return okHttpClient.newCall(requestBuilder.build()).execute();
	}

	/**
	 * 异步 POST 请求，使用 JSON 格式作为参数
	 *
	 * @param url       请求地址
	 * @param json      JSON 格式参数
	 * @param myNetCall 回调函数
	 * @throws IOException 异常
	 */
	public void postJsonAsync(String url, String json, final MyNetCall myNetCall) throws IOException {
		RequestBody body = RequestBody.create(JSON, json);
		// 构造 Request
		buildRequest(url, myNetCall, body);
	}

	/**
	 * 构造 POST 请求参数
	 *
	 * @param bodyParams 请求参数
	 * @return {@link RequestBody}
	 */
	private RequestBody setRequestBody(Map<String, Object> bodyParams) {
		RequestBody body = null;
		okhttp3.FormBody.Builder formEncodingBuilder = new okhttp3.FormBody.Builder();
		if (bodyParams != null) {
			Iterator<String> iterator = bodyParams.keySet().iterator();
			String key = "";
			while (iterator.hasNext()) {
				key = iterator.next().toString();
				formEncodingBuilder.add(key, bodyParams.get(key).toString());
			}
		}
		body = formEncodingBuilder.build();
		return body;
	}

	/**
	 * 构造 Request 发起异步请求
	 *
	 * @param url       请求地址
	 * @param myNetCall 回调函数
	 * @param body      {@link RequestBody}
	 */
	private void buildRequest(String url, MyNetCall myNetCall, RequestBody body) {
		Request.Builder requestBuilder = new Request.Builder();
		Request request = requestBuilder.post(body).url(url).build();
		// 将 Request 封装为 Call
		Call call = okHttpClient.newCall(request);
		// 执行 Call
		call.enqueue(new Callback() {
			@Override
			public void onFailure(Call call, IOException e) {
				myNetCall.failed(call, e);
			}

			@Override
			public void onResponse(Call call, Response response) throws IOException {
				myNetCall.success(call, response);
			}
		});
	}

	/**
	 * 自定义网络回调接口
	 */
	public interface MyNetCall {
		/**
		 * 请求成功的回调处理
		 *
		 * @param call     {@link Call}
		 * @param response {@link Response}
		 * @throws IOException 异常
		 */
		void success(Call call, Response response) throws IOException;

		/**
		 * 请求失败的回调处理
		 *
		 * @param call {@link Call}
		 * @param e    异常
		 */
		void failed(Call call, IOException e);
	}
}
